package bender.appl.networks;

import bender.BenderView;
import bender.appl.io.ArchivoBinarioInputSynapse;
import bender.appl.io.ArchivoReductorVectorConverter;
import bender.appl.io.Converter;
import bender.appl.io.PosicionVectorConverter;
import bender.appl.layers.CapaEntradaBender;
import bender.appl.layers.CapaSalidaBender;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.joone.engine.KohonenSynapse;
import org.joone.engine.Synapse;
import org.joone.helpers.factory.JooneTools;
import org.joone.net.NeuralNet;

/**
 * Red usada para el test de Bender
 * @author Ramiro
 */
public class RedBender extends RedBasica {

    /**
     * Serialización. Evita warnings
     */
    private static final long serialVersionUID = 2328736552097579615L;
    private Map<Integer, String> entrenamientos;
    private BenderView view = null;
    Synapse conexion;

    /**
     * Crea la red
     */
    public RedBender(BenderView view) {
        entrenamientos = new HashMap<Integer, String>();
        this.view = view;
        agregarCapas();
        setConexiones();
        setConfiguracionMonitor();
        addListeners();
    }

    public static RedBender cargarRed(BenderView view, String path) throws IOException, ClassNotFoundException {
        RedBender red = (RedBender) JooneTools.load(path);
        red.view = view;
        return red;


    }

     private void addListeners() {
        this.getMonitor().addNeuralNetListener(view);
    }

    /**
     * Agrega las capas de input y output
     */
    private void agregarCapas() {
        this.addLayer(CapaEntradaBender.getInstance(), NeuralNet.INPUT_LAYER);
        this.addLayer(CapaSalidaBender.getInstance(), NeuralNet.OUTPUT_LAYER);
    }

    /**
     * Conecta las dos capas
     */
    private void setConexiones() {
        conexion = new KohonenSynapse();
        this.getInputLayer().addOutputSynapse(conexion);
        this.getOutputLayer().addInputSynapse(conexion);
    }

    /**
     * Configura el monitor
     */
    private void setConfiguracionMonitor() {
        this.getMonitor().setLearningRate(0.7);
        this.getMonitor().setMomentum(0.5);
    }

    /**
     * Indica el archivo de entrada
     * @param nombre nombre del archivo de entrada
     */
    public void setArchivoEntrada(String nombre) {
        Synapse entrada = new ArchivoBinarioInputSynapse(nombre);
        this.getInputLayer().removeAllInputs();
        this.getInputLayer().addInputSynapse(entrada);
    }

    public void agregarEntrenamiento(String path, int resultado) {
        entrenamientos.put(new Integer(resultado), path);
    }

    public void entrenar(Integer epochs) throws IOException {
        int i = 0;
        double[][] input = new double[entrenamientos.size()][];
        //double[][] outputEsperado = new double[entrenamientos.size()][];

        for (Map.Entry<Integer, String> entrada : entrenamientos.entrySet()) {
            Converter convertidorArchivo = new ArchivoReductorVectorConverter(entrada.getValue());
            //PosicionVectorConverter convertidorSalida = new PosicionVectorConverter(entrada.getKey());
            input[i] = convertidorArchivo.convertir();
            //outputEsperado[i] = convertidorSalida.convertir();
            i++;
        }

        JooneTools.train_unsupervised(this, input, epochs.intValue(), 1, view, true);
    }

    public int reconocer(String path) throws IOException {
        Converter convertidorArchivo = new ArchivoReductorVectorConverter(path);
        double[] salida = JooneTools.interrogate(this, convertidorArchivo.convertir());
        PosicionVectorConverter convertidorVectorConverter = new PosicionVectorConverter(salida);
        return convertidorVectorConverter.convertirRetro();
    }

    public void guardar(String path) throws IOException {
        BufferedWriter archivoGuardar = new BufferedWriter(new FileWriter("pesos.txt"));
        StringBuffer str = new StringBuffer();
        double[][] valores = conexion.getWeights().value;
        for (int i = 0; i < valores.length; i++) {
            str.append("\n");
            for (int j = 0; j < valores[i].length; j++) {
                Double valor = new Double(valores[i][j]);
                str.append( valor.floatValue()+ " ||\t ");
            }
        }
        String strEscribir =str.toString();
        archivoGuardar.write(strEscribir, 0, strEscribir.length());

    }
}
